<html>
<head>
<title>NotSoSimpleSudoku</title>
<script src="jquery.js"> </script>
<script>
try{
	var cl = console.log,
		ce = console.error,
		json = JSON.stringify;
	function clj( e){
		console.log(JSON.stringify(e));
	}
} catch(e) {
	var cl=ce=json=clj=function(){};
}

function $$(id){
	return $(document.getElementById(id.replace(/#(\w+)/i,'$1')));
}

var worker = {
	worker:undefined,
	handlers:[],
	count:0,
	init: function( filename){
		this.worker = new Worker(filename||"worker.js");
		this.worker.onmessage = this.catchMessage;
	},
	request: function( cmd, args, callback, context){
		this.worker.postMessage({
			cmd:cmd,
			args:args,
			refid:this.count
		});
		
		this.handlers.push({
			refid:this.count,
			callback:callback,
			context:context
		});
		this.count++;
	},
	
	catchMessage: function(e){
		if( e.data.cmd == "note"){
			cl( e.data.args.value);
			return;
		}
		if( e.data.cmd=="log"){
			cl( e.data.args.value);
			return;
		}
		if( e.data.cmd=="error"){
			ce( e.data.args.value);
			return;
		}if( e.data.cmd=="logerror"){
			ce( eval(e.data.args.value));
			return;
		}
		var temp= worker.handlers.filter( function( handle){
			return handle.refid == e.data.refid;
		});
		if( temp.length == 0){
			ce( "no handler found.");
			ce( e.data);
			return;
		}
		if( temp.length > 1)
			throw "Too many handlers.";
		temp[0].callback.call(temp[0].context, e.data);
	}

};

var sdkGFX = {
	zoom : 1,
	init: function(){
		worker.request("get_multiple", {values:["candidIDs","cellIDs","cellsToHouses"]}, this.createField);
		worker.request("get_multiple", {values:["cellOffsets","candidOffsets"]}, this.getOffsets);
	},
	
	createField : function(e){
		var cellsToHouses = e.args.cellsToHouses,
			insert = $("<div></div>"),
			cellDummy = $('<div></div>').addClass('cell'),
			candidDummy = $('<span></span>').addClass("candid").click(function(e){
				if( $(this).attr('only') != 'only')
					sdkGFX.click( this.id);
				else
					sdkGFX.dblclick( this.id);
			}).dblclick(function(e){
				if( $(this).attr('only') != 'only')
					sdkGFX.dblclick( this.id);
				else
					sdkGFX.dblclick( this.id);
			});
		
		e.args.candidIDs.forEach(function(candid){
			cellDummy.append( candidDummy.clone(true).attr('candidID',candid).attr('id','_'+candid).html(candid));
		});
		
		e.args.cellIDs.forEach( function( cellID){
			insert.append( cellDummy
				.clone(true)
				.attr({
					id: cellID, 
					houses: cellsToHouses[cellID].join(' ')
				})
				.children()
					.attr("id",function(){
							return cellID+this.id;
						})
				.end()
				);
		});
		
		$$("sdkWrapper").empty().append(insert.children());
		
		var toolDummy = $('<span></span>').addClass('button').click(function(e){
				sdkGFX.highlight($(this).attr('candidid'));
			}),
			insert = $("<div></div>");
			
		e.args.candidIDs.forEach(function(candidID){
			insert.append( toolDummy.clone(true).attr('candidid',candidID).html(candidID));
		});
		
		$$('toolWrapper').empty().append( insert.children());
	},
	
	getOffsets: function(e){
		var cellOffsets = e.args.cellOffsets,
			candidOffsets = e.args.candidOffsets,
			style = $('<style></style>');
		
		for( var candidID in candidOffsets){
			style.append(".candid[candidid='"+candidID+"']{\n top: "+candidOffsets[candidID].y+"%;\nleft: "+candidOffsets[candidID].x+"%;\n}\n");
		}
		
		for( var cellID in cellOffsets){
			style.append("#"+cellID+" {\n\ttop: "+cellOffsets[cellID].y+"%;\n\tleft: "+cellOffsets[cellID].x+"%;\n}\n");
		}
		
		$$('#workerstyle').append(style.text());
	},
	
	click: function(id){
		id=id.split('_');
		worker.request("change",{value:"swap",cellID:id[0],candidID:id[1]}, this.redrawField, this);
	},
	
	dblclick: function( id){
		id=id.split('_');
		worker.request("change",{value:"only",cellID:id[0],candidID:id[1]}, this.redrawField, this);	
	},
	
	redrawField: function(e){
		e.args.value.forEach(function(cell){
			var candids = $$(cell.id)
				.children()
				.attr('enabled','enabled')
				.removeAttr('only')
				.filter( function( candidElem){
					return !~cell.candids.indexOf( $(this).attr('candidid'))
				})
					.removeAttr('enabled')
				.end()
				.filter("[enabled]");
			if( candids.size() == 1)
				candids.attr('only','only');
		});
	},
	
	load: function( sz){
		worker.request("init",{field: sz},this.redrawField,this);
	},
	
	highlight: function(candidID){
		$("span.candid").removeAttr('highlight').filter("[candidid='"+candidID+"']").attr('highlight','highlight');
	}
};



$(function() {
	worker.init("worker.js");	
	sdkGFX.init();
	
	$$('nakedsingle').click(function(e){
		worker.request("calc",{value:"Naked Single"}, sdkGFX.redrawField, sdkGFX);
	});
});
</script>
<style id="default" lang="CSS">
#toolWrapper {
	width:450px;
	height: 50px;
}

.button {
	width: 30px;
	border: outset;
}

#sdkWrapper {
	position: relative;
	width: 450px;
	height: 450px;
	background: white;
}

.cell {
	border: gray thin solid;
	width: 11.22%;
	height: 11.22%;
	position: absolute;
	background: white;
}

.candid {
	font-family: monospace;
	font-size: 100%;
	position: absolute;
}



.candid:not([enabled]) {
	color: #DDDDDD;
}



</style>
<style id="workerstyle">

</style>
<style id="randstyle">
.candid[only] {
	font-family: 'Comic Sans MS';
	font-size: 300%;
	left: 20%;
	top: -20%;
	position: absolute;
	z-index:1;
}

.candid[highlight][enabled]:not([only]) {
	background: limegreen;
}
</style>
</head>
<body>
<div id="toolWrapper"></div>
<div id="sdkWrapper"></div>

<br/><br/>
<input id="inputfield" type="text" value="600030008008906300003000100800167003000000000200854007009000400006308500700040001" /> <a id="loader" href="javascript:sdkGFX.load($$('inputfield').val())">Load</a>

<br/>
<a href="javascript:void(0);" id="nakedsingle">Naked Single</a>
<a href="javascript:void(0);" id="hiddensingle">Hidden Single</a>

</body>
</html>